feat: add StreamResponse and SSE response factories#10395
Conversation
memleakd
left a comment
There was a problem hiding this comment.
Thanks for working on this, looks useful! I left a few comments:
memleakd
left a comment
There was a problem hiding this comment.
Thanks for the updates. I left two small follow-up comments.
memleakd
left a comment
There was a problem hiding this comment.
Thanks for the quick updates. Looks good to me!
There was a problem hiding this comment.
Pull request overview
Adds first-class streaming HTTP response support to CodeIgniter’s response layer by introducing a new StreamResponse type and building SSEResponse on top of it, plus corresponding response-service factories and documentation.
Changes:
- Added
StreamResponse(streaming body via callback or iterable chunks) and refactoredSSEResponseto extend it while enforcing SSE headers and skipping CSP finalization. - Extended
ResponseInterface/ResponseTraitwithstream()andeventStream()factories (including protocol version preservation) and added system tests for send/headers behavior. - Updated the user guide and v4.8.0 changelog to document streaming responses and the new factory methods, including examples.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| system/HTTP/StreamResponse.php | Introduces StreamResponse with streaming callback/chunk support and streaming-aware send() implementation. |
| system/HTTP/SSEResponse.php | Refactors SSE to extend StreamResponse, enforce SSE headers, and disable CSP finalization. |
| system/HTTP/ResponseTrait.php | Adds stream() and eventStream() factories; changes shouldFinalizeCsp() visibility to support subclass control. |
| system/HTTP/ResponseInterface.php | Requires new stream() and eventStream() methods for framework-wide response implementations. |
| tests/system/HTTP/StreamResponseTest.php | Adds unit tests for StreamResponse behaviors and response factory return types. |
| tests/system/HTTP/StreamResponseSendTest.php | Adds separate-process tests validating headers/cookies/body streaming behavior and CSP handling. |
| tests/system/HTTP/SSEResponseSendTest.php | Adds a regression test for HTTP/2 protocol header behavior via eventStream() factory. |
| user_guide_src/source/outgoing/response.rst | Adds “Streaming Responses” documentation and updates SSE section to reference stream-based design and factories. |
| user_guide_src/source/outgoing/response/036.php | Updates SSE example to use $this->response->eventStream(). |
| user_guide_src/source/outgoing/response/037.php | Updates SSE example to use $this->response->eventStream() and then set headers. |
| user_guide_src/source/outgoing/response/038.php | Updates SSE example to use $this->response->eventStream(). |
| user_guide_src/source/outgoing/response/039.php | Adds streaming callback example with batched writes and flush. |
| user_guide_src/source/outgoing/response/040.php | Adds iterable/generator chunks streaming example. |
| user_guide_src/source/outgoing/response/041.php | Adds upstream proxy streaming example (chunked forwarding). |
| user_guide_src/source/changelogs/v4.8.0.rst | Updates changelog entries to reflect general streaming support + new factories and interface requirements. |
| structarmed.php | Updates struct-armed configuration to account for the new StreamResponse class. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public function testConstructorTreatsCallableIterableAsCallable(): void | ||
| { | ||
| // An array callable is both callable and iterable; callable must win. | ||
| $response = new StreamResponse($this->writeHello(...)); | ||
| $response->pretend(); |
| public function stream(callable|iterable $callbackOrChunks): StreamResponse | ||
| { | ||
| return (new StreamResponse($callbackOrChunks))->setProtocolVersion($this->getProtocolVersion()); | ||
| } | ||
|
|
||
| /** | ||
| * Creates a response for streaming Server-Sent Events (SSE). | ||
| * | ||
| * @param callable(SSEResponse): void $callback | ||
| */ | ||
| public function eventStream(callable $callback): SSEResponse | ||
| { | ||
| return (new SSEResponse($callback))->setProtocolVersion($this->getProtocolVersion()); | ||
| } |
There was a problem hiding this comment.
These two silently discards state the previous response might have stored. For example, $this->response()->setHeader(...)->stream() will return a StreamResponse without the header sent. Same for ->setCookie(). If this is desired, we need to document somewhere that the previously set headers/cookies etc needs to be re-added on the returned stream or SSE response.
|
I asked claude to review this and I got this: [Medium] Streaming responses get recorded as previous_url() — the NonBufferedResponseInterface marker is inert system/CodeIgniter.php:555-556 — Failure scenario: An SSEResponse is served at |
Description
This PR adds first-class support for streaming HTTP responses. It introduces
StreamResponsefor responses generated progressively, such as large exports, proxied streams, or token-style output, and buildsSSEResponseon top of it for Server-Sent Events. Streaming responses can be created from the response service withstream()andeventStream().Checklist: